#include "calculate_soc.h"
#include "bq76920.h"

#define CELL_NUM BQ76920_CELL_NUM

// 三元锂电池 SOC 开路电压法计算数据表
uint16_t SocOcvTab[101] =
    {
        3282,                                                       // 0%~1%
        3309, 3334, 3357, 3378, 3398, 3417, 3434, 3449, 3464, 3477, // 0%~10%
        3489, 3500, 3510, 3520, 3528, 3536, 3543, 3549, 3555, 3561, // 11%~20%
        3566, 3571, 3575, 3579, 3583, 3586, 3590, 3593, 3596, 3599, // 21%~30%
        3602, 3605, 3608, 3611, 3615, 3618, 3621, 3624, 3628, 3632, // 31%~40%
        3636, 3640, 3644, 3648, 3653, 3658, 3663, 3668, 3674, 3679, // 41%~50%
        3685, 3691, 3698, 3704, 3711, 3718, 3725, 3733, 3741, 3748, // 51%~60%
        3756, 3765, 3773, 3782, 3791, 3800, 3809, 3818, 3827, 3837, // 61%~70%
        3847, 3857, 3867, 3877, 3887, 3897, 3908, 3919, 3929, 3940, // 71%~80%
        3951, 3962, 3973, 3985, 3996, 4008, 4019, 4031, 4043, 4055, // 81%~90%
        4067, 4080, 4092, 4105, 4118, 4131, 4145, 4158, 4172, 4185, // 91~100%
};

float capacity_rated = 2.0;
float capacity_remain;
float soc_ocv;
float soc_ah;

/**
 * @brief 查找一个数在数组中的右侧边界(二分法)
 *
 * @param nums：待查询数组
 * @param start_pos：起始位置
 * @param end_pos：结束位置
 * @param target：目标数据
 * @return -1：表示不存在这个数
 */
static int right_bound(uint16_t *nums, uint8_t start_pos, uint8_t end_pos, uint16_t target)
{
    uint16_t left  = start_pos;
    uint16_t right = end_pos;

    while (left < right)
    {
        int mid = (left + right) / 2;

        if (nums[mid] == target)
        {
            left = mid + 1; // 注意
        }
        else if (nums[mid] < target)
        {
            left = mid + 1;
        }
        else if (nums[mid] > target)
        {
            right = mid;
        }
    }
    if ((left - 1) < start_pos)
    {
        return -1;
    }
    return left - 1; // 注意
}

/**
 * @brief 根据单体电芯最低电压计算出soc值,用于上电和长时间静止状态下的校准
 *
 * @param voltage：用于估算的电压值
 * @return soc：电压对应的SOC
 */
static uint16_t OcvToSoc(uint16_t voltage)
{
    uint16_t soc = 0;
    uint16_t index;

    if (voltage <= SocOcvTab[0])
    {
        soc = 0;
    }
    else if (voltage >= SocOcvTab[100])
    {
        soc = 1000;
    }
    else
    {
        index = right_bound(SocOcvTab, 0, 100, voltage);

        if (voltage == SocOcvTab[index])
        {
            // 整数SOC值
            soc = index * 10;
        }
        else
        {
            // 计算百分比后的小数点
            soc = index * 10 + ((SocOcvTab[index] - voltage) * 10) / (SocOcvTab[index] - SocOcvTab[index + 1]);
        }
    }

    return soc;
}

static uint32_t GetLowVoltage(uint32_t *voltage)
{
    uint8_t i;
    uint32_t lowV;

    // 最小单体电压
    lowV = voltage[0];
    for (i = 1; i < CELL_NUM; i++)
    {
        if (voltage[i] < lowV)
        {
            lowV = voltage[i];
        }
    }

    return lowV;
}

/**
 * @brief OCV估算SOC
 *
 */
void OCVCalculateSOC(uint32_t *voltage)
{
    uint32_t lowV = GetLowVoltage(voltage);

    // 开路电压法soc计算
    soc_ocv = OcvToSoc(lowV) / 1000.0f;

    // 限制SOC在0%-100%之间
    if (soc_ocv > 1.0f)
    {
        soc_ocv = 1.0f;
    }
    else if (soc_ocv < 0.0f)
    {
        soc_ocv = 0.0f;
    }

    // 剩余容量 = 实际容量 * soc
    capacity_remain = capacity_rated * soc_ocv;
}

/**
 * @brief AH积分法估算SOC
 *
 */
void BQ76920_SocAHCalculate(float current)
{
    float capacity_value;

    // 安时积分法soc计算
    // abs取绝对值，除3600把 A/S 单位换算成  A/H
    capacity_value = fabs(current) / 1000.0f / 3600;
    // printf("capacity_value=%f\r\n", capacity_value);
    capacity_remain -= capacity_value;
    // printf("capacity_remain=%f\r\n", capacity_remain);
    soc_ah = capacity_remain / capacity_rated;

    // 限制SOC在0%-100%之间
    if (soc_ah > 1.0f)
    {
        soc_ah = 1.0f;
    }
    else if (soc_ah < 0.0f)
    {
        soc_ah = 0.0f;
    }
}